using System;
using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;

namespace FireStarter.Base
{
    public static class StringUtilities
    {
        private static readonly NameValueCollection myPluralRules;
        private static readonly string[] myUnaffectedPlural;
        private static readonly NameValueCollection myIrregularPlural;
        private static readonly NameValueCollection mySingularRules;
        private static readonly string[] myUnaffectedSingular;
        private static readonly NameValueCollection myIrregularSingular;

        static StringUtilities()
        {
            myPluralRules = new NameValueCollection();
            myPluralRules["(s)tatus$"] = "$1tatuses";
            myPluralRules["^(ox)$"] = "$1en"; // ox
            myPluralRules["([m|l])ouse$"] = "$1ice"; // mouse, louse
            myPluralRules["(matr|vert|ind)ix|ex$"] = "$1ices"; // matrix, vertex, index
            myPluralRules["(x|ch|ss|sh)$"] = "$1es"; // search, switch, fix, box, process, address
            myPluralRules["([^aeiouy]|qu)y$"] = "$1ies"; // query, ability, agency
            myPluralRules["(hive)$"] = "$1s"; // archive, hive
            myPluralRules["(?:([^f])fe|([lr])f)$"] = "$1$2ves"; // half, safe, wife
            myPluralRules["sis$"] = "ses"; // basis, diagnosis
            myPluralRules["([ti])um$"] = "$1a"; // datum, medium
            myPluralRules["(p)erson$"] = "$1eople"; // person, salesperson
            myPluralRules["(m)an$"] = "$1en"; // man, woman, spokesman
            myPluralRules["(c)hild$"] = "$1hildren"; // child
            myPluralRules["(buffal|tomat)o$"] = "$1oes"; // buffalo, tomato, (potato ?)
            myPluralRules["us$"] = "uses"; // us
            myPluralRules["(alias)"] = "$1es"; // alias
            myPluralRules["(octop|vir)us$"] = "$1i";
            // (never use ?)octopus, virus - virus has no defined plural (according to Latin/dictionary.com), but viri is better than viruses/viruss
            myPluralRules["(ax|cri|test)is$"] = "$1es"; // axis, crisis
            myPluralRules["s$"] = "s"; // no change (compatibility)
            myPluralRules["$"] = "s";

            myUnaffectedPlural = new[]
                                     {
                                         "^(.*[nrlm]ese)$", "^(.*deer)$", "^(.*fish)$", "^(.*measles)$", "^(.*ois)$",
                                         "^(.*pox)$", "^(.*sheep)$", "^(Amoyese)$", "^(bison)$", "^(Borghese)$",
                                         "^(bream)$", "^(breeches)$", "^(britches)$", "^(buffalo)$", "^(cantus)$",
                                         "^(carp)$", "^(chassis)$", "^(clippers)$", "^(cod)$", "^(coitus)$", "^(Congoese)$"
                                         , "^(contretemps)$", "^(corps)$", "^(debris)$", "^(diabetes)$", "^(djinn)$",
                                         "^(eland)$", "^(elk)$", "^(equipment)$", "^(Faroese)$", "^(flounder)$",
                                         "^(Foochowese)$", "^(gallows)$", "^(Genevese)$", "^(Genoese)$", "^(Gilbertese)$",
                                         "^(graffiti)$", "^(headquarters)$", "^(herpes)$", "^(hijinks)$",
                                         "^(Hottentotese)$", "^(information)$", "^(innings)$", "^(jackanapes)$",
                                         "^(Kiplingese)$", "^(Kongoese)$", "^(Lucchese)$", "^(mackerel)$", "^(Maltese)$",
                                         "^(mews)$", "^(moose)$", "^(mumps)$", "^(Nankingese)$", "^(news)$", "^(nexus)$",
                                         "^(Niasese)$", "^(Pekingese)$", "^(Piedmontese)$", "^(pincers)$", "^(Pistoiese)$",
                                         "^(pliers)$", "^(Portuguese)$", "^(proceedings)$", "^(rabies)$", "^(rice)$",
                                         "^(rhinoceros)$", "^(salmon)$", "^(Sarawakese)$", "^(scissors)$",
                                         "^(sea[- ]bass)$", "^(series)$", "^(Shavese)$", "^(shears)$", "^(siemens)$",
                                         "^(species)$", "^(swine)$", "^(testes)$", "^(trousers)$", "^(trout)$", "^(tuna)$",
                                         "^(Vermontese)$", "^(Wenchowese)$", "^(whiting)$", "^(wildebeest)$",
                                         "^(Yengeese)$"
                                     };

            myIrregularPlural = new NameValueCollection();
            myIrregularPlural[@"(.*)\b(atlas)$"] = "atlases";
            myIrregularPlural[@"(.*)\b(beef)$"] = "beefs";
            myIrregularPlural[@"(.*)\b(brother)$"] = "brothers";
            myIrregularPlural[@"(.*)\b(child)$"] = "children";
            myIrregularPlural[@"(.*)\b(corpus)$"] = "corpuses";
            myIrregularPlural[@"(.*)\b(cow)$"] = "cows";
            myIrregularPlural[@"(.*)\b(ganglion)$"] = "ganglions";
            myIrregularPlural[@"(.*)\b(genie)$"] = "genies";
            myIrregularPlural[@"(.*)\b(genus)$"] = "genera";
            myIrregularPlural[@"(.*)\b(graffito)$"] = "graffiti";
            myIrregularPlural[@"(.*)\b(hoof)$"] = "hoofs";
            myIrregularPlural[@"(.*)\b(loaf)$"] = "loaves";
            myIrregularPlural[@"(.*)\b(man)$"] = "men";
            myIrregularPlural[@"(.*)\b(money)$"] = "monies";
            myIrregularPlural[@"(.*)\b(mongoose)$"] = "mongooses";
            myIrregularPlural[@"(.*)\b(move)$"] = "moves";
            myIrregularPlural[@"(.*)\b(mythos)$"] = "mythoi";
            myIrregularPlural[@"(.*)\b(numen)$"] = "numina";
            myIrregularPlural[@"(.*)\b(occiput)$"] = "occiputs";
            myIrregularPlural[@"(.*)\b(octopus)$"] = "octopuses";
            myIrregularPlural[@"(.*)\b(opus)$"] = "opuses";
            myIrregularPlural[@"(.*)\b(ox)$"] = "oxen";
            myIrregularPlural[@"(.*)\b(penis)$"] = "penises";
            myIrregularPlural[@"(.*)\b(person)$"] = "people";
            myIrregularPlural[@"(.*)\b(sex)$"] = "sexes";
            myIrregularPlural[@"(.*)\b(soliloquy)$"] = "soliloquies";
            myIrregularPlural[@"(.*)\b(testis)$"] = "testes";
            myIrregularPlural[@"(.*)\b(trilby)$"] = "trilbys";
            myIrregularPlural[@"(.*)\b(turf)$"] = "turfs";

            mySingularRules = new NameValueCollection();
            mySingularRules["(s)tatuses$"] = "$1tatus";
            mySingularRules["(matr)ices$"] = "$1ix";
            mySingularRules["(vert|ind)ices$"] = "$1ex";
            mySingularRules["^(ox)en"] = "$1";
            mySingularRules["(alias)es$"] = "$1";
            mySingularRules["([octop|vir])i$"] = "$1us";
            mySingularRules["(cris|ax|test)es$"] = "$1is";
            mySingularRules["(shoe)s$"] = "$1";
            mySingularRules["(o)es$"] = "$1";
            mySingularRules["uses$"] = "us";
            mySingularRules["([m|l])ice$"] = "$1ouse";
            mySingularRules["(x|ch|ss|sh)es$"] = "$1";
            mySingularRules["(m)ovies$"] = "$1ovie";
            mySingularRules["(s)eries$"] = "$1eries";
            mySingularRules["([^aeiouy]|qu)ies$"] = "$1y";
            mySingularRules["([lr])ves$"] = "$1f"; // ?
            mySingularRules["(tive)s$"] = "$1";
            mySingularRules["(hive)s$"] = "$1";
            mySingularRules["(drive)s$"] = "$1";
            mySingularRules["([^f])ves$"] = "$1fe";
            mySingularRules["(^analy)ses$"] = "$1sis";
            mySingularRules["((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$"] = "$1$2sis";
            mySingularRules["([ti])a$"] = "$1um";
            mySingularRules["(p)eople$"] = "$1erson";
            mySingularRules["(m)en$"] = "$1an";
            mySingularRules["(c)hildren$"] = "$1hild";
            mySingularRules["(n)ews$"] = "$1ews";
            mySingularRules["s$"] = "";

            myUnaffectedSingular = new[]
                                       {
                                           "^(.*[nrlm]ese)$", "^(.*deer)$", "^(.*fish)$", "^(.*measles)$", "^(.*ois)$",
                                           "^(.*pox)$", "^(.*sheep)$", "^(.*us)$", "^(.*ss)$", "^(Amoyese)$", "^(bison)$",
                                           "^(Borghese)$", "^(bream)$", "^(breeches)$", "^(britches)$", "^(buffalo)$",
                                           "^(cantus)$", "^(carp)$", "^(chassis)$", "^(clippers)$", "^(cod)$", "^(coitus)$"
                                           , "^(Congoese)$", "^(contretemps)$", "^(corps)$", "^(debris)$", "^(diabetes)$",
                                           "^(djinn)$", "^(eland)$", "^(elk)$", "^(equipment)$", "^(Faroese)$",
                                           "^(flounder)$", "^(Foochowese)$", "^(gallows)$", "^(Genevese)$", "^(Genoese)$",
                                           "^(Gilbertese)$", "^(graffiti)$", "^(headquarters)$", "^(herpes)$",
                                           "^(hijinks)$", "^(Hottentotese)$", "^(information)$", "^(innings)$",
                                           "^(jackanapes)$", "^(Kiplingese)$", "^(Kongoese)$", "^(Lucchese)$",
                                           "^(mackerel)$", "^(Maltese)$", "^(mews)$", "^(moose)$", "^(mumps)$",
                                           "^(Nankingese)$", "^(news)$", "^(nexus)$", "^(Niasese)$", "^(Pekingese)$",
                                           "^(Piedmontese)$", "^(pincers)$", "^(Pistoiese)$", "^(pliers)$",
                                           "^(Portuguese)$", "^(proceedings)$", "^(rabies)$", "^(rice)$", "^(rhinoceros)$",
                                           "^(salmon)$", "^(Sarawakese)$", "^(scissors)$", "^(sea[- ]bass)$", "^(series)$",
                                           "^(Shavese)$", "^(shears)$", "^(siemens)$", "^(species)$", "^(swine)$",
                                           "^(testes)$", "^(trousers)$", "^(trout)$", "^(tuna)$", "^(Vermontese)$",
                                           "^(Wenchowese)$", "^(whiting)$", "^(wildebeest)$", "^(Yengeese)$"
                                       };

            myIrregularSingular = new NameValueCollection();
            myIrregularSingular[@"(.*)\b(atlases)$"] = "atlas";
            myIrregularSingular[@"(.*)\b(beefs)$"] = "beef";
            myIrregularSingular[@"(.*)\b(brothers)$"] = "brother";
            myIrregularSingular[@"(.*)\b(children)$"] = "child";
            myIrregularSingular[@"(.*)\b(corpuses)$"] = "corpus";
            myIrregularSingular[@"(.*)\b(cows)$"] = "cow";
            myIrregularSingular[@"(.*)\b(ganglions)$"] = "ganglion";
            myIrregularSingular[@"(.*)\b(genies)$"] = "genie";
            myIrregularSingular[@"(.*)\b(genera)$"] = "genus";
            myIrregularSingular[@"(.*)\b(graffiti)$"] = "graffito";
            myIrregularSingular[@"(.*)\b(hoofs)$"] = "hoof";
            myIrregularSingular[@"(.*)\b(loaves)$"] = "loaf";
            myIrregularSingular[@"(.*)\b(men)$"] = "man";
            myIrregularSingular[@"(.*)\b(monies)$"] = "money";
            myIrregularSingular[@"(.*)\b(mongooses)$"] = "mongoose";
            myIrregularSingular[@"(.*)\b(moves)$"] = "move";
            myIrregularSingular[@"(.*)\b(mythoi)$"] = "mythos";
            myIrregularSingular[@"(.*)\b(numina)$"] = "numen";
            myIrregularSingular[@"(.*)\b(occiputs)$"] = "occiput";
            myIrregularSingular[@"(.*)\b(octopuses)$"] = "octopus";
            myIrregularSingular[@"(.*)\b(opuses)$"] = "opus";
            myIrregularSingular[@"(.*)\b(oxen)$"] = "ox";
            myIrregularSingular[@"(.*)\b(penises)$"] = "penis";
            myIrregularSingular[@"(.*)\b(people)$"] = "person";
            myIrregularSingular[@"(.*)\b(sexes)$"] = "sex";
            myIrregularSingular[@"(.*)\b(soliloquies)$"] = "soliloquy";
            myIrregularSingular[@"(.*)\b(testes)$"] = "testis";
            myIrregularSingular[@"(.*)\b(trilbys)$"] = "trilby";
            myIrregularSingular[@"(.*)\b(turfs)$"] = "turf";
        }

        public static string Singularize(string input)
        {
            if (input == String.Empty) return input;

            if (myUnaffectedSingular.Any(rule => Regex.IsMatch(input, rule, RegexOptions.IgnoreCase)))
            {
                return input;
            }
            var singularised = Singularise(input, myIrregularSingular);
            if (singularised != null) return singularised;

            singularised = Singularise(input, mySingularRules);
            return singularised ?? input;
        }

        private static string Singularise(string input, NameValueCollection singularisationRules)
        {
            var singularisationRule = singularisationRules
                .Cast<string>()
                .Where(rule => Regex.IsMatch(input, rule, RegexOptions.IgnoreCase))
                .FirstOrDefault();
            if (singularisationRule != null)
            {
                return Regex.Replace(input, singularisationRule, singularisationRules[singularisationRule],
                                     RegexOptions.IgnoreCase);
            }
            return null;
        }

        public static string Pluralize(string input)
        {
            if (input == String.Empty)
                return input;
            foreach (string rule in myUnaffectedPlural)
            {
                if (Regex.IsMatch(input, rule, RegexOptions.IgnoreCase))
                    return input;
            }
            foreach (string rule in myIrregularPlural)
            {
                if (rule.Equals(input, StringComparison.InvariantCultureIgnoreCase))
                    return myIrregularPlural[rule];
            }
            foreach (string rule in myPluralRules)
            {
                if (Regex.IsMatch(input, rule, RegexOptions.IgnoreCase))
                    return Regex.Replace(input, rule, myPluralRules[rule], RegexOptions.IgnoreCase);
            }
            return String.Empty;
        }

        public static string Camelize(string input)
        {
            var replace = Humanize(input);
            replace = replace.Replace(" ", "");
            return replace;
        }
        /// <summary>
        /// Replaces all _ with whites space
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string Humanize(string input)
        {
            string replace = input.Replace("_", " ");
            replace = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(replace);
            return replace;
        }
        /// <summary>
        /// Create a classification of the input this is a Singular of the input
        ///   Camel cased.
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string Classify(string input)
        {
            return Camelize(Singularize(input));
        }
        /// <summary>
        /// Pascal Cases a table name that has as seperator '_', '-' or ' ' in it so that the 
        ///  the pascal casing will be done as  follows first letter is capitalised
        ///  the first letter that follows a seperator is capitalised. All other letters are left unchanged.
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static string PascalCaseTableName(string text)
        {
            if (text.Length > 1)
            {
                text = text[0].ToString().ToUpper() +
                       text.Substring(1);
                text = RemovePascalDelimiters(text, "_");
                text = RemovePascalDelimiters(text, " ");
                text = RemovePascalDelimiters(text, "-");
            }
            else
            {
                text = text.ToUpper();
            }
            return text;
        }
        private static string RemovePascalDelimiters(string text, string delimiter)
        {
            int pos = text.IndexOf(delimiter);
            while (pos != -1 && pos < text.Length - 1)
            {
                text = text.Substring(0, pos) + text[pos + 1].ToString().ToUpper()
                       + text.Substring(pos + 2);
                pos = text.IndexOf(delimiter, pos);
            }
            return text;
        }
        /// <summary>
        /// Determines whether the word is already pascal cased. 
        /// This will return true when.
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static bool IsManyPascalWords(string text)
        {
            if (text.Length <= 1) return false;
            string firstLetter = text.Substring(0,1);
            if (firstLetter == firstLetter.ToLowerInvariant()) return false;

            string textRemainderPart = text.Substring(1);
            return textRemainderPart != textRemainderPart.ToLower();
        }

        public static string RemovePrefix(string prefix, string text)
        {
            if (!string.IsNullOrEmpty(prefix) && text.StartsWith(prefix, true, CultureInfo.CurrentCulture))
            {
                text = text.Substring(prefix.Length);
            }
            return text;
        }

        public static string ToLowerFirstLetter(string input)
        {
            char firstLetter = input[0];
            return Char.ToLower(firstLetter) + input.Substring(1);
        }

        //private static string Prn(DMRelationshipProperty relationshipProperty)
        //{
        //    if (relationshipProperty == null) return "null";
        //    return string.Format("{0}({1})", Prn(relationshipProperty.RelationshipPropertyID), Prn(relationshipProperty.RelationshipID));
        //}

        //public static string Prn(Guid? guid)
        //{
        //    if (!guid.HasValue) return "null";
        //    return guid.Value.ToString().Substring(0, 4);
        //}
    }
}